package expression;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.FunctionMissing;
import com.googlecode.aviator.lexer.token.OperatorType;
import com.googlecode.aviator.runtime.JavaMethodReflectionFunctionMissing;
import com.googlecode.aviator.runtime.RuntimeUtils;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorDecimal;
import com.googlecode.aviator.runtime.type.AviatorNumber;
import com.googlecode.aviator.runtime.type.AviatorObject;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * @Author Administrator
 * @create 2021/10/28 9:50
 */
public class AviatorDemo {
    public static void main(String[] args) {
        Long result = (Long) AviatorEvaluator.execute("1+2+3");
        System.out.println(result);
    }

    public static void compile() {
        String expression = "a-(b-c)>100";
        // 编译表达式
        Expression compiledExp = AviatorEvaluator.compile(expression);

        Map<String, Object> env = new HashMap<String, Object>();
        env.put("a", 100.3);
        env.put("b", 45);
        env.put("c", -199.100);

        // 执行表达式
        Boolean result = (Boolean) compiledExp.execute(env);
        System.out.println(result);
    }

    public static void javaMethodFunctions() throws IllegalAccessException, NoSuchMethodException {
        // Import string's instance methods as custom functions under namespace 's'.
        AviatorEvaluator.addInstanceFunctions("s", String.class);
        AviatorEvaluator.execute("println(s.indexOf('hello', 'l'))");
        AviatorEvaluator.execute("println(s.replaceAll('hello', 'l', 'x'))");

        // Import StringUtils static methods as custom functions under namespace 'sutil'.
        AviatorEvaluator.addStaticFunctions("sutil", StringUtils.class);
        System.out.println(AviatorEvaluator.execute("sutil.isEmpty('hello')"));
        System.out.println(AviatorEvaluator.execute("sutil.isEmpty('')"));
    }

    public static void javaReflectionFunctionMissing() {
        // Enable function missing based on java instance methods reflection.
        AviatorEvaluator.setFunctionMissing(JavaMethodReflectionFunctionMissing.getInstance());
        // Calling String#indexOf by reflection
        System.out.println(AviatorEvaluator.execute("indexOf('hello world', 'w')"));
        // Calling Long#floatValue by reflection
        System.out.println(AviatorEvaluator.execute("floatValue(3)"));
        // Calling BigDecimal#add
        System.out.println(AviatorEvaluator.execute("add(3M, 4M)"));
    }

    public static void function() {
        System.out.println(AviatorEvaluator.execute("sysdate()"));
        System.out.println(AviatorEvaluator.execute("rand()"));
        System.out.println(AviatorEvaluator.execute("now()"));
        System.out.println(AviatorEvaluator.execute("date_to_string(sysdate(),'yyyy-MM-dd')"));
        System.out.println(AviatorEvaluator
                .execute("string_to_date(date_to_string(sysdate(),'yyyy-MM-dd'),'yyyy-MM-dd')"));

        // string function
        System.out.println("test string function...");
        System.out.println(AviatorEvaluator.execute("string.length('hello')"));
        System.out.println(AviatorEvaluator.execute("string.contains('hello','h')"));
        System.out.println(AviatorEvaluator.execute("string.startsWith('hello','h')"));
        System.out.println(AviatorEvaluator.execute("string.endsWith('hello','llo')"));
        System.out.println(
                AviatorEvaluator.execute("string.contains(\"test\",string.substring('hello',1,2))"));
        System.out.println(Arrays
                .toString((String[]) AviatorEvaluator.execute("string.split('hello world,aviator',' ')")));

        // math function
        System.out.println("test math function...");
        System.out.println(AviatorEvaluator.execute("math.abs(-3)"));
        System.out.println(AviatorEvaluator.execute("math.pow(-3,2)"));
        System.out.println(AviatorEvaluator.execute("math.sqrt(14.0)"));
        System.out.println(AviatorEvaluator.execute("math.log(100)"));
        System.out.println(AviatorEvaluator.execute("math.log10(1000)"));
        System.out.println(AviatorEvaluator.execute("math.sin(20)"));
        System.out.println(AviatorEvaluator.execute("math.cos(99.23)"));
        System.out.println(AviatorEvaluator.execute("math.tan(19.9)"));

        // seq lib
        Map<String, Object> env = new HashMap<String, Object>();
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(3);
        list.add(100);
        list.add(-100);
        env.put("list", list);
        System.out.println(AviatorEvaluator.execute("reduce(list,+,0)", env));
        System.out.println(AviatorEvaluator.execute("filter(list,seq.exists())", env));
        System.out.println(AviatorEvaluator.execute("count(list)", env));
        System.out.println(AviatorEvaluator.execute("include(list,100)", env));
        System.out.println(AviatorEvaluator.execute("sort(list)", env));
        System.out.println(AviatorEvaluator.execute("map(list,println)", env));
        System.out.println(list);
    }

    private static class TestFunctionMissing implements FunctionMissing {

        @Override
        public AviatorObject onFunctionMissing(final String name, final Map<String, Object> env,
                                               final AviatorObject... args) {
            // Returns the function name.
            System.out.println(
                    "Function not found, name=" + name + ", env=" + env + ", args=" + Arrays.toString(args));
            return FunctionUtils.wrapReturn(name);
        }

    }

    public static void functionMissing() {
        // Set function missing handler.
        AviatorEvaluator.setFunctionMissing(new TestFunctionMissing());

        System.out.println(AviatorEvaluator.execute("test(1,2,3)"));
        System.out.println(AviatorEvaluator.execute("not_found(1,2,3)"));
    }

    public static class OverloadDivide extends AbstractFunction {

        @Override
        public String getName() {
            return "/";
        }

        @Override
        public AviatorObject call(final Map<String, Object> env, final AviatorObject arg1,
                                  final AviatorObject arg2) {
            if (FunctionUtils.getNumberValue(arg2, env).doubleValue() == 0) {
                return new AviatorDecimal(0);
            }
            BigDecimal left = AviatorNumber.valueOf(arg1.getValue(env)).toDecimal(env);
            BigDecimal right = AviatorNumber.valueOf(arg2.getValue(env)).toDecimal(env);
            return AviatorDecimal.valueOf(left.divide(right, RuntimeUtils.getMathContext(env)));
        }

    }

    public static void opOverload() {
        AviatorEvaluator.addOpFunction(OperatorType.DIV, new OverloadDivide());

        System.out.println(AviatorEvaluator.execute("4 - 1/0 + 3"));
        System.out.println(AviatorEvaluator.execute("4 - 1/2.0 + 3"));
    }

    public static void lambda() {
        String exp = "a=1; b = lambda(x) -> a+ x end ; a=4 ; b(5)";
        System.out.println(AviatorEvaluator.execute(exp)); // output 6

        Map<String, Object> env = new HashMap<String, Object>();
        env.put("x", 1);
        env.put("y", 2);
        env.put("z", 3);

        AviatorEvaluator.defineFunction("test",
                "lambda(x) -> lambda(y) -> lambda(z) -> x + y + z end end end");
        System.out.println(AviatorEvaluator.execute("test(4)(5)(6)", env)); // output 15

        env.put("a", 4);
        System.out.println(AviatorEvaluator.execute("test(4)(5)(6) + a", env)); // output 19
    }

    public static void collection() {
        final List<String> list = new ArrayList<String>();
        list.add("hello");
        list.add(" world");

        final int[] array = new int[3];
        array[0] = 0;
        array[1] = 1;
        array[2] = 3;

        final Map<String, Date> map = new HashMap<String, Date>();
        map.put("date", new Date());

        Map<String, Object> env = new HashMap<String, Object>();
        env.put("list", list);
        env.put("array", array);
        env.put("mmap", map);

        System.out.println(AviatorEvaluator.execute(
                "list[0]+list[1]+'\narray[0]+array[1]+array[2]='+(array[0]+array[1]+array[2]) +' \ntoday is '+mmap.date ",
                env));
    }

    public static void seq() {
        int[] a = new int[10];
        for (int i = 0; i < 10; i++) {
            a[i] = i;
        }

        Map<String, Object> env = AviatorEvaluator.newEnv("a", a);

        System.out.println(AviatorEvaluator.execute("a[1] + 100", env));
        System.out.println(AviatorEvaluator.execute("'a[1]=' + a[1]", env));
        System.out.println(AviatorEvaluator.execute("count(a)", env));
        System.out.println(AviatorEvaluator.execute("reduce(a,+,0)", env));
        System.out.println(AviatorEvaluator.execute("seq.every(a,seq.gt(0))", env));
        System.out
                .println(AviatorEvaluator.execute("seq.every(a,seq.and(seq.ge(0), seq.lt(10)))", env));
        System.out
                .println(AviatorEvaluator.execute("seq.not_any(a,seq.and(seq.ge(0), seq.lt(10)))", env));
        System.out
                .println(AviatorEvaluator.execute("seq.not_any(a,seq.and(seq.lt(0), seq.ge(10)))", env));
        System.out.println(AviatorEvaluator.execute("seq.some(a,seq.eq(3))", env));
        System.out.println(AviatorEvaluator.execute("seq.some(a, lambda(x) -> x > 10 end)", env));
    }

}
